import { type App } from 'vue';
export class AppEventSource<EventSourceMap extends Record<string, any>> {
  private subscribers?: Map<keyof EventSourceMap, Set<EventSubscriber<any, any>>>;

  /** 移除所有订阅者 */
  public clear() {
    if (this.subscribers) {
      this.subscribers.clear();
    }
  }

  /**
   * @description 为EventSource实例添加事件订阅者。
   * @param eventName 想要监听的事件名称
   * @param callback 广播事件时调用的订阅者函数
   * @returns () => void 取消订阅回调。
   */
  public subscribe<EventName extends keyof EventSourceMap>(
    eventName: EventName,
    callback: EventSubscriber<EventSourceMap[EventName], EventName>,
  ): UnsubscribeCallback {
    if (!this.subscribers) {
      this.subscribers = new Map();
    }
    const callbackSet = this.subscribers.get(eventName);
    if (!callbackSet) {
      this.subscribers.set(eventName, new Set([callback]));
    } else {
      callbackSet.add(callback);
    }
    return () => {
      this.unsubscribe(eventName, callback);
    };
  }

  /** 删除事件订阅者
   @param eventName 不再想要监听的事件名称
   @param callback 引用已订阅的回调函数
   */
  public unsubscribe<EventName extends keyof EventSourceMap>(
    eventName: EventName,
    callback: EventSubscriber<EventSourceMap[EventName], EventName>,
  ) {
    if (this.subscribers) {
      const callbackSet = this.subscribers.get(eventName);
      if (callbackSet) {
        callbackSet.delete(callback);
      }
    }
  }

  /** 发送给定类型的事件
   @param eventName 要发出的事件类型
   @param event 事件中要传递的数据
   */
  public emit<EventName extends keyof EventSourceMap>(
    eventName: EventName,
    event?: EventSourceMap[EventName],
  ) {
    if (this.subscribers) {
      const callbackSet = this.subscribers.get(eventName);
      if (callbackSet) {
        callbackSet.forEach((cb) => {
          cb(event, eventName);
        });
      }
    } else {
      console.error(`请先调用AppEventSource.subscribe[${String(eventName)}]创建监听者`);
    }
  }
}

export const appEventSource = new AppEventSource<AppGlobalEventSourceMap>();

export function createEventSource(app: App) {
  app.provide<AppEventSource<AppGlobalEventSourceMap>>(
    Symbol.for('appEventSource'),
    appEventSource,
  );
}
